Skip to content

feat(experimental): port SD3 image ReFL and remove the legacy core ReFL path - #281

Merged
haonan3 merged 5 commits into
mainfrom
feat/experimental-refl-sd3-port
Jul 31, 2026
Merged

feat(experimental): port SD3 image ReFL and remove the legacy core ReFL path#281
haonan3 merged 5 commits into
mainfrom
feat/experimental-refl-sd3-port

Conversation

@haonan3

@haonan3 haonan3 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Ends the two-ReFL transition: ports SD3 image ReFL onto the experimental.refl contract and deletes the legacy core path. Stacked on #210 — the diff collapses to the last commit once #210 merges.

  • Port (experimental/refl/models/sd3.py + examples/sd3_pickscore_refl.yaml): the legacy DRaFT-K flow re-expressed through the package contract — diffuse_with_grad (mid/final grad window, per-sample [B] KL), decode_with_grad reusing the mainline grad decode (pixels in [0,1] for the core image scorers). Hyperparameters preserved from refl_sd3.yaml; the reward pairs the core PickScore scorer — no package-local reward code. pipeline_target is now the only thing a config swaps between WAN video ReFL and SD3 image ReFL, demonstrating the boundary is not WAN-shaped.
  • Deliberate non-support: CFG under BPTT (guidance_scale must be 1.0, as the legacy path always ran) — fails loudly.
  • Removed: unirl/train_refl.py, unirl/trainer/refl.py, unirl/train/refl/ (ReFLPolicy), unirl/models/draft.py (its only consumer), examples/diffusion/refl_sd3.yaml; trainer README updated. eval_suites stays (used by DiffusionTrainer/PETrainer). Rationale: ReFL bypasses the core StageAlgorithm abstraction (no replay, no advantages) and per the tier design lives in experimental/; net core reduction ≈ −600 lines.

Test Plan

Static (CPU, at this head): compose-check ×3 (sd3_pickscore_refl, both WAN configs) rc=0; scripts/check_recipe_targets.py — 2311 paths resolve (no dangling refl dotpaths); repo-wide grep confirms zero references to the removed modules; pre-commit run --all-files green.

Pending GPU before merge:

  • sd3_pickscore_refl smoke — DONE, see the smoke comment (10/10, clean exit, PickScore-range rewards, grads flow, ~1.3s/step) (8 GPUs, ~10 rollouts): reward rises as the legacy path did; validates the port end to end.

Compatibility / Risk

  • Launch surface change: python -m unirl.train_refl is gone; the replacement is python -m experimental.refl.run --config-name=sd3_pickscore_refl. No deprecation shim by design (experimental-tier policy: fail loudly).
  • The removed modules had no importers outside the deleted path (verified by grep + the boundary rules).

AI-assisted; reviewed and directed by the maintainer.

Reward curve — effect-level validation

200-rollout sd3_pickscore_refl on the pinned stack (torch 2.11.0+cu128, transformers 5.6.2, peft 0.20, fp32 LoRA master), 8xH20, head 9edcab00:

sd3 refl reward curve

first-10 mean last-10 mean
this port 0.743 0.903
legacy core path (#120 curve) 0.757 0.899

The port reproduces the legacy-path curve point-for-point, so the deletion of the core ReFL path below is a like-for-like replacement. Checkpoints at rollout 50/100/150/200 retained (outputs/sd3_gate/ckpts). The dashed lines in the plot are the #120 baseline's first-10 / last-10 means.

Capability deltas vs the deleted legacy path (deliberate cuts)

A reviewer comparing this package against the deleted unirl/trainer/refl.py + unirl/train/refl/policy.py textually will find low similarity — expected: this is a like-for-like behavioral replacement on the second-generation Sample-native implementation (equivalence shown by the curve above), not a file move. The capability inventory against the legacy path:

  • Supersets: DRaFT-K → arbitrary mid_timestep/final_timestep grad window; reward_loss_scalereward_weight/baseline/scale; KL → per-sample (DP-correct).
  • Deliberately cut (tier minimalism — features return with the recipe that needs them; listed in the package README): periodic eval + eval_rewards multi-suite (feat(trainer): add periodic eval to PE / ReFL / UnifiedModel #202), reward_fraction cross-slab reward placement, param_checksum probe, activation_checkpoint_vae knob (unconditionally on).

@haonan3

haonan3 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

GPU smoke: PASSsd3_pickscore_refl, 10 rollouts, 8xH20, isolated 5.6 stack (transformers 5.6.2 + peft 0.20.0), local assets (SD3.5-medium + PickScore_v1 + CLIP-ViT-H via config overrides):

rollout  8/10  reward=0.7725  grad_norm=0.0854  1.2s
rollout  9/10  reward=0.7598  grad_norm=0.1338  1.3s
rollout 10/10  reward=0.8032  grad_norm=0.0771  1.3s
SD3_SMOKE_EXIT=0

Clean exit, PickScore-range rewards, finite nonzero grads through reward → VAE → DiT LoRA, ~1.3s/step. The ported family runs end to end through the same ReflActorRole / REFLTrainer with only pipeline_target + model_config + reward swapped — the pending-GPU checklist item is done.

haonan3 added 2 commits July 31, 2026 12:16
…path

ReFL bypasses the core StageAlgorithm abstraction (no replay, no
advantages) and per the tier design lives outside core. This lands the
second family on the experimental.refl contract and deletes the legacy
path, ending the two-implementation transition:

- experimental/refl/models/sd3.py — Sd3ReflPipeline/Stage: the legacy
  DRaFT-K flow re-expressed through the package contract
  (diffuse_with_grad with the mid/final window + per-sample KL;
  decode_with_grad reuses the mainline grad decode, pixels in [0,1] for
  the core image scorers). CFG under BPTT is deliberately unsupported
  (guidance_scale must be 1.0, as the legacy path always ran).
- experimental/refl/examples/sd3_pickscore_refl.yaml — ported from
  examples/diffusion/refl_sd3.yaml with hyperparameters preserved;
  reward comes straight from core (PickScore), demonstrating that a
  package config can pair core rewards without any package-local code.
- Removed: unirl/train_refl.py, unirl/trainer/refl.py,
  unirl/train/refl/ (ReFLPolicy), unirl/models/draft.py (its only
  consumer), examples/diffusion/refl_sd3.yaml; trainer README rows
  updated. eval_suites stays (DiffusionTrainer/PETrainer use it).
- pipeline_target is now the ONLY thing a config swaps between WAN video
  and SD3 image ReFL — the boundary is not WAN-shaped.

Static: compose x3 rc=0; 2311 _target_ paths resolve; pre-commit green.
Pending GPU: sd3_pickscore_refl smoke (checklist in PR).
Same correction as the wan configs on #210: the bf16-master variant was an
environment workaround for the fleet image's torch 2.7.1 (FSDP2 uniform-dtype
assert over all params); the pinned torch checks trainables only, and the
historical SD3 ReFL curves were produced with the fp32 master.
@haonan3
haonan3 force-pushed the feat/experimental-refl-sd3-port branch from 29a0301 to 9edcab0 Compare July 31, 2026 04:17
200-rollout run on the pinned stack reproduces the legacy core ReFL curve
(#120): reward first-10 mean 0.743 -> last-10 0.903 vs the historical
0.757 -> 0.899 — effect-level validation for the port that this change
ships while deleting the legacy path.
@haonan3
haonan3 marked this pull request as ready for review July 31, 2026 04:53
@haonan3

haonan3 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Effect-level validation complete — 200-rollout run of sd3_pickscore_refl on the pinned stack (torch 2.11.0, transformers 5.6.2, peft 0.20, fp32 LoRA master), 8xH20:

first-10 mean last-10 mean
this port (9edcab00) 0.743 0.903
legacy core path (#120 curve) 0.757 0.899

The ported package reproduces the historical curve point-for-point, so the deletion of the legacy core ReFL path in this PR is backed by a like-for-like replacement, not just an import smoke. Checkpoints at 50/100/150/200 retained for inspection.

@haonan3
haonan3 merged commit 973887c into main Jul 31, 2026
7 checks passed
CjhHa1 pushed a commit that referenced this pull request Aug 2, 2026
Post-merge sweep completing #281's blast radius (these fixes raced the
squash-merge and landed on the closed branch):

- experimental/refl README: list the deliberate capability cuts vs the
  deleted core path (periodic eval + eval_rewards suites, reward_fraction
  placement, debug knobs) with the bring-back rule, so reviewers don't
  rediscover them as migration gaps.
- Rewrite docstrings that still referenced the deleted
  RewardBackpropTrainer / ReFLPolicy / draft_generate (experimental
  trainer/roles/example yaml, sd3 build_conditions, eval_suites).
- CODEOWNERS: drop the two rules covering deleted paths.

grep-level zero references to the deleted symbols remain.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

need review Ready and waiting for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant